﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.Identity;
using Volo.Abp.TenantManagement;

namespace GMall.AdministrationService.Menus
{
    public class Menu : CreationAuditedAggregateRoot<Guid>
    {
        public virtual string Name { get; protected set; }
        public virtual string DisplayName { get; protected set; }
        public virtual string Code { get; protected set; }
        public virtual string Url { get; protected set; }
        public virtual string Api { get; protected set; }
        public virtual string Icon { get; protected set; }
        public virtual int Order { get; protected set; }
        public virtual MenuType Type { get; protected set; }
        public virtual bool IsActive { get; protected set; }
        public virtual bool IsVisible { get; protected set; }
        public virtual Guid? ParentId { get; protected set; }
        public virtual string CustomData { get; protected set; }
        protected Menu() { }
        public Menu(Guid id, string name, string displayName, string url, string api, string icon, int order, MenuType type, bool isActive = true, bool isVisible = true, Guid? parentId = null, string customData = null) : base(id)
        {
            SetName(name);
            SetDisplayName(displayName);
            SetUrl(url);
            SetApi(api);
            SetIcon(icon);
            SetOrder(order);
            SetType(type);
            SetIsActive(isActive);
            SetIsVisible(isVisible);
            SetParentId(parentId);
            SetCustomData(customData);
        }
        public void Update(string name, string displayName, string url, string api, string icon, int order, MenuType type, bool isActive, bool isVisible, string customData)
        {
            SetName(name);
            SetDisplayName(displayName);
            SetUrl(url);
            SetApi(api);
            SetIcon(icon);
            SetOrder(order);
            SetType(type);
            SetIsActive(isActive);
            SetIsVisible(isVisible);
            SetCustomData(customData);
        }

        #region setter
        public Menu SetName(string name)
        {
            Check.NotNullOrWhiteSpace(name, "name");
            Name = name;
            return this;
        }
        public Menu SetDisplayName(string displayName)
        {
            Check.NotNullOrWhiteSpace(displayName, "displayName");
            DisplayName = displayName;
            return this;
        }
        public Menu SetCode(string code)
        {
            Check.NotNullOrWhiteSpace(code, "code");
            Code = code;
            return this;
        }
        public Menu SetUrl(string url)
        {
            Url = url;
            return this;
        }
        public Menu SetApi(string api)
        {
            Api = api;
            return this;
        }
        public Menu SetIcon(string icon)
        {
            Icon = icon;
            return this;
        }
        public Menu SetOrder(int order)
        {
            Order = order;
            return this;
        }
        public Menu SetIsActive(bool isActive)
        {
            IsActive = isActive;
            return this;
        }
        public Menu SetIsVisible(bool isVisible)
        {
            IsVisible = isVisible;
            return this;
        }
        public Menu SetType(MenuType type)
        {
            Type = type;
            return this;
        }
        public Menu SetParentId(Guid? parentId)
        {
            ParentId = parentId;
            return this;
        }
        public Menu SetCustomData(string customData)
        {
            CustomData = customData;
            return this;
        }
        #endregion

        #region code
        /// <summary>
        /// Creates code for given numbers.
        /// Example: if numbers are 4,2 then returns "00004.00002";
        /// </summary>
        /// <param name="numbers">Numbers</param>
        public static string CreateCode(params int[] numbers)
        {
            if (numbers.IsNullOrEmpty())
            {
                return null;
            }
            return numbers.Select(number => number.ToString(new string('0', 5))).JoinAsString(".");
        }
        public static string AppendCode(string parentCode, string childCode)
        {
            if (childCode.IsNullOrEmpty())
            {
                throw new ArgumentNullException(nameof(childCode), "childCode can not be null or empty.");
            }
            if (parentCode.IsNullOrEmpty())
            {
                return childCode;
            }
            return parentCode + "." + childCode;
        }
        /// <summary>
        /// Calculates next code for given code.
        /// Example: if code = "00019.00055.00001" returns "00019.00055.00002".
        /// </summary>
        /// <param name="code">The code.</param>
        public static string CalculateNextCode(string code)
        {
            if (code.IsNullOrEmpty())
            {
                throw new ArgumentNullException(nameof(code), "code can not be null or empty.");
            }
            var parentCode = GetParentCode(code);
            var lastUnitCode = GetLastUnitCode(code);
            return AppendCode(parentCode, CreateCode(Convert.ToInt32(lastUnitCode) + 1));
        }
        /// <summary>
        /// Gets parent code.
        /// Example: if code = "00019.00055.00001" returns "00019.00055".
        /// </summary>
        /// <param name="code">The code.</param>
        public static string GetParentCode(string code)
        {
            if (code.IsNullOrEmpty())
            {
                throw new ArgumentNullException(nameof(code), "code can not be null or empty.");
            }
            var splittedCode = code.Split('.');
            if (splittedCode.Length == 1)
            {
                return null;
            }
            return splittedCode.Take(splittedCode.Length - 1).JoinAsString(".");
        }
        /// <summary>
        /// Gets the last unit code.
        /// Example: if code = "00019.00055.00001" returns "00001".
        /// </summary>
        /// <param name="code">The code.</param>
        public static string GetLastUnitCode(string code)
        {
            if (code.IsNullOrEmpty())
            {
                throw new ArgumentNullException(nameof(code), "code can not be null or empty.");
            }
            var splittedCode = code.Split('.');
            return splittedCode[splittedCode.Length - 1];
        }
        public static string GetRelativeCode(string code, string parentCode)
        {
            if (code.IsNullOrEmpty())
            {
                throw new ArgumentNullException(nameof(code), "code can not be null or empty.");
            }

            if (parentCode.IsNullOrEmpty())
            {
                return code;
            }

            if (code.Length == parentCode.Length)
            {
                return null;
            }

            return code.Substring(parentCode.Length + 1);
        }
        #endregion
    }
}
